/*
 * Copyright 2021 Shulie Technology, Co.Ltd
 * Email: shulie@shulie.io
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.shulie.surge.data.runtime.disruptor;

/**
 * Coordinates claiming sequences for access to a data structure while tracking dependent {@link Sequence}s
 */
public interface Sequencer extends Cursored
{
	/** Set to -1 as sequence starting point */
	long INITIAL_CURSOR_VALUE = -1L;

	/**
	 * The capacity of the data structure to hold entries.
	 *
	 * @return the size of the RingBuffer.
	 */
	int getBufferSize();

	/**
	 * Has the buffer got capacity to allocate another sequence.  This is a concurrent
	 * method so the response should only be taken as an indication of available capacity.
	 * @param requiredCapacity in the buffer
	 * @return true if the buffer has the capacity to allocate the next sequence otherwise false.
	 */
	boolean hasAvailableCapacity(final int requiredCapacity);

	/**
	 * Claim the next event in sequence for publishing.
	 * @return the claimed sequence value
	 */
	long next();

	/**
	 * Claim the next n events in sequence for publishing.  This is for batch event producing.  Using batch producing
	 * requires a little care and some math.
	 * <pre>
	 * int n = 10;
	 * long hi = sequencer.next(n);
	 * long lo = hi - (n - 1);
	 * for (long sequence = lo; sequence &lt;= hi; sequence++) {
	 *     // Do work.
	 * }
	 * sequencer.publish(lo, hi);
	 * </pre>
	 *
	 * @param n the number of sequences to claim
	 * @return the highest claimed sequence value
	 */
	long next(int n);

	/**
	 * Attempt to claim the next event in sequence for publishing.  Will return the
	 * number of the slot if there is at least <code>requiredCapacity</code> slots
	 * available.
	 * @return the claimed sequence value
	 * @throws InsufficientCapacityException
	 */
	long tryNext() throws InsufficientCapacityException;

	/**
	 * Attempt to claim the next n events in sequence for publishing.  Will return the
	 * highest numbered slot if there is at least <code>requiredCapacity</code> slots
	 * available.  Have a look at {@link Sequencer#next()} for a description on how to
	 * use this method.
	 *
	 * @param n the number of sequences to claim
	 * @return the claimed sequence value
	 * @throws InsufficientCapacityException
	 */
	long tryNext(int n) throws InsufficientCapacityException;

	/**
	 * Get the remaining capacity for this sequencer.
	 * @return The number of slots remaining.
	 */
	long remainingCapacity();

	/**
	 * Claim a specific sequence.  Only used if initialising the ring buffer to
	 * a specific value.
	 *
	 * @param sequence The sequence to initialise too.
	 */
	void claim(long sequence);

	/**
	 * Publishes a sequence. Call when the event has been filled.
	 *
	 * @param sequence
	 */
	void publish(long sequence);

	/**
	 * Batch publish sequences.  Called when all of the events have been filled.
	 *
	 * @param lo first sequence number to publish
	 * @param hi last sequence number to publish
	 */
	void publish(long lo, long hi);

	/**
	 * Confirms if a sequence is published and the event is available for use; non-blocking.
	 *
	 * @param sequence of the buffer to check
	 * @return true if the sequence is available for use, false if not
	 */
	boolean isAvailable(long sequence);

	/**
	 * Add the specified gating sequences to this instance of the Disruptor.  They will
	 * safely and atomically added to the list of gating sequences.
	 *
	 * @param gatingSequences The sequences to add.
	 */
	void addGatingSequences(Sequence... gatingSequences);

	/**
	 * Remove the specified sequence from this sequencer.
	 *
	 * @param sequence to be removed.
	 * @return <tt>true</tt> if this sequence was found, <tt>false</tt> otherwise.
	 */
	boolean removeGatingSequence(Sequence sequence);

	/**
	 * Create a new SequenceBarrier to be used by an EventProcessor to track which messages
	 * are available to be read from the ring buffer given a list of sequences to track.
	 *
	 * @see SequenceBarrier
	 * @param sequencesToTrack
	 * @return A sequence barrier that will track the specified sequences.
	 */
	SequenceBarrier newBarrier(Sequence... sequencesToTrack);

	/**
	 * Get the minimum sequence value from all of the gating sequences
	 * added to this ringBuffer.
	 *
	 * @return The minimum gating sequence or the cursor sequence if
	 * no sequences have been added.
	 */
	long getMinimumSequence();

	long getHighestPublishedSequence(long sequence, long availableSequence);
}